﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Data.Caching;
using System.Reflection;

using Pg.BioMedics.SDR.Caching.Linq;

namespace Pg.BioMedics.SDR.Caching
{
    /// <summary>
    /// Base class for all data caches. This class provides logic common.
    /// for all caches. This class supports LINQ queries.
    /// </summary>
    public abstract class BaseCache<T>
    {
        #region Fields

        private readonly ICachingStrategy<T> cachingStrategy;

        #endregion

        #region Constructors

        /// <summary>
        /// Provides base cache initialization with the given caching strategy.
        /// </summary>
        /// <param name="cachingStrategy">CachingStrategy utilized by the cache</param>
        internal protected BaseCache(ICachingStrategy<T> cachingStrategy)
        {
            this.cachingStrategy = cachingStrategy; 
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets a queriable object which may be used to query against this 
        /// cache
        /// </summary>
        public IQueryable<T> Query
        {
            get
            {
                return this.cachingStrategy.GetQueryable();
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Puts an item of the generic type into the cache
        /// </summary>
        /// <param name="item">Cache item</param>
        public void Put(T item)
        {
            // input params validation
            if (item == null)
                throw new CachingException("Couldnot put an item of type {0} into the cache, the item cannot be null", typeof(T));

            try
            {
                this.cachingStrategy.Put(GetKey(item), item);
            }
            catch (Exception ex)
            {
                throw new CachingException("Couldnot put an item {0}:{1} into the cache", ex, typeof(T).Name, GetKey(item));
            }
        }

        /// <summary>
        /// Removes given item from the cache
        /// </summary>
        /// <param name="item">Item</param>
        public void Remove(T item)
        {
            try
            {
                this.cachingStrategy.Remove(GetKey(item));
            }
            catch (Exception ex)
            {
                throw new CachingException("Couldnot remove item {0} from cache by item {1}", ex, typeof(T).Name, item);
            }
        }

        /// <summary>
        /// Removes all entities from the cache
        /// </summary>
        public void Clear()
        {
            try
            {
                this.cachingStrategy.Clear();
            }
            catch (Exception ex)
            {
                throw new CachingException("Could not clear the cache", ex);
            }
        }

        #endregion

        #region Abstract methods
        
        /// <summary>
        /// Gets entity key value. This key will be used to
        /// identify entity in the cache region.
        /// </summary>
        /// <param name="item">Entity instance</param>
        /// <returns>Key value</returns>
        public abstract string GetKey(T item);

        #endregion              
    }
}
